package jp.ddo.trismegistos.servlet;

import java.io.IOException;
import java.util.Map;
import java.util.UUID;

import javax.servlet.ServletContext;
import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;

import jp.ddo.trismegistos.RsStatics;
import jp.ddo.trismegistos.action.Action;
import jp.ddo.trismegistos.annotation.LoginCheck;
import jp.ddo.trismegistos.container.ActionContainer;
import jp.ddo.trismegistos.dao.DaoManager;

import org.apache.commons.lang.StringUtils;
import org.apache.log4j.Logger;

/**
 * リクエストURLに応じて、実行Actionクラスをコントロールするクラス。
 * 
 * @author y_sugasawa
 * @since 2011/05/28
 */
@WebServlet(name = "ControlServlet", urlPatterns = { "*.do" })
public class ControlServlet extends HttpServlet {

	/** シリアルID */
	private static final long serialVersionUID = 1L;

	/** ログ */
	private static final Logger log = Logger.getLogger(ControlServlet.class
			.getName());

	/**
	 * {@inheritDoc}
	 */
	@Override
	public void doPost(final HttpServletRequest request,
			final HttpServletResponse response) throws IOException,
			ServletException {

		log.debug("リクエストURL:" + request.getRequestURI());
		final HttpSession session = request.getSession();
		final ServletContext application = getServletContext();
		@SuppressWarnings("unchecked")
		final Map<Object, Object> master = (Map<Object, Object>) application
				.getAttribute("master");

		final String encoding = (String) master.get(RsStatics.ENCODING);
		request.setCharacterEncoding(encoding);
		setContentType(response, encoding);

		final String defaultUrl = (String) master.get("jspPath");

		// TODO セッション消去的なものがほしい

		if (submitCheck(request, session) == false) {
			request.setAttribute("err", "ダブルサブミットエラーです。");
			forward(defaultUrl + "error/error.jsp", request, response);
			return;
		}

		final String url = convertUrl(request);
		Action action = ActionContainer.getInstaince().getAction(url,
				(DaoManager) application.getAttribute(RsStatics.DAO_MANAGER));

		if (action == null) {
			request.setAttribute("err", "不正なURLです。");
			forward(defaultUrl + "error/error.jsp", request, response);
			return;
		}

		// ログインチェック
		if (action.getClass().getAnnotation(LoginCheck.class) != null) {
			if (loginCheck(session) == false) {
				forward(defaultUrl + "login.jsp", request, response);
				return;
			}
		}

		request.getSession().setAttribute("token",
				UUID.randomUUID().toString() + System.currentTimeMillis());

		action.setHttpServletRequest(request);
		action.setHttpSession(session);
		action.setServletContext(application);

		action.validate();
		if (action.check() == false) {
			log.debug("validate error");
			forward(defaultUrl + request.getParameter("return"), request,
					response);
			return;
		}

		String returnPath = null;
		try {
			returnPath = action.execute();
		} catch (final Exception e) {
			e.printStackTrace();
		}

		log.debug(defaultUrl + returnPath);
		forward(defaultUrl + returnPath, request, response);
	}

	/**
	 * {@inheritDoc}
	 */
	@Override
	public void doGet(final HttpServletRequest request,
			final HttpServletResponse response) throws IOException,
			ServletException {
		doPost(request, response);
	}

	/**
	 * ダブルサブミットチェックを行う。
	 * 
	 * @param request
	 *            リクエスト情報
	 * @param session
	 *            セッション情報
	 * @return ダブルサブミットならばflase, それ以外はtrue
	 */
	protected boolean submitCheck(final HttpServletRequest request,
			final HttpSession session) {

		final String token = request.getParameter("token");
		if (token == null) {
			return true;
		}

		if (token.equals(session.getAttribute("token"))) {
			session.removeAttribute("token");
			return true;
		} else {
			return false;
		}
	}

	/**
	 * リクエストURLを変換する。
	 * 
	 * @param request
	 *            HTTPリクエスト
	 * @return 実行するActionクラスを指定するための文字列
	 */
	protected String convertUrl(final HttpServletRequest request) {
		return request.getRequestURL().toString().replaceAll(
				"http://" + request.getServerName() + ".*"
						+ request.getContextPath() + "/", StringUtils.EMPTY)
				.replaceAll("\\.do", StringUtils.EMPTY);
	}

	/**
	 * アプリケーションで使用する文字コードをセットする。
	 * 
	 * @param response
	 *            HTTPレスポンス
	 */
	protected void setContentType(final HttpServletResponse response,
			final String encoding) {
		response.setContentType("text/html; charset=" + encoding);
	}

	/**
	 * ログインしているかどうかのチェックを行う。
	 * 
	 * @param session
	 *            セッション情報
	 * @return ログインしていればtrue, していなければfalse
	 */
	protected boolean loginCheck(final HttpSession session) {
		return session.getAttribute(RsStatics.USER_INFO) != null;
	}

	/**
	 * 指定したURLへフォワードする。
	 * 
	 * @param url
	 *            フォワード先
	 * @param request
	 *            HTTPリクエスト
	 * @param response
	 *            HTTPレスポンス
	 */
	protected void forward(final String url, final HttpServletRequest request,
			final HttpServletResponse response) throws IOException,
			ServletException {
		getServletContext().getRequestDispatcher(url)
				.forward(request, response);
	}
}
